package com.tunan.system.att.web;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import com.tunan.system.att.error.AttachmentNotFoundMainError;
import com.tunan.system.att.utils.BscAttForm;
import com.tunan.utils.AttUtils;
import com.tunan.utils.JsonUtils;


/**
 * 附件上传基类
 */
@Controller
public class BscBaseAttController{
	public static Logger logger = LoggerFactory.getLogger(BscBaseAttController.class);
	/**
	 * 缓冲大小
	 */
	protected static final int BUFFER_SIZE = 2048; 
	
	
	//－－－－－－－－－－－
	/**
	 * 单个将附件上传至服务器磁盘的同时向附件表插入附件记录
	 * @param tableName 业务表对应的VO对象名
	 * @param recordId 业务表记录主键
	 * @param atPath 附件上传路径
	 * @param atCode 附件编号，如为null则自动以时间作为编号
	 * @param atName 附件名称，如为null则使用fileForm对象的文件名属性值
	 * @param atType 附件类型，允许为null
	 * @param attachment 附件对象
	 * @return 返回布尔型上传结果
	 */
	public boolean uploadFile(String tableName, String recordId,String pkName,String atPath,String orgSeq,String isRelative,
			String atCode, String atName, String atType,String isDbStore,String isEncrypt,String encryptClass, MultipartFile attachment)throws Exception{
		//构造Form对象
		BscAttForm form =null;
		return uploadFile(form, attachment);
	}
	/**
	 * 单个将附件上传至服务器磁盘的同时向附件表插入附件记录
	 * @param form 附件Form对象
	 * @param attachment 附件对象
	 * @return 返回上传结果
	 */
	public boolean uploadFile(BscAttForm form, MultipartFile attachment)throws Exception{
		boolean result = false;
		
		if(form != null){
			boolean success =false;
			boolean isDb="1".equals(form.getIsDbStore());
			byte[] bt=null;

			 if(attachment == null|| attachment.getSize() == 0){
				 return false;
			 }
			 //数据库存储时
			 if(isDb){
				try {
					if("1".equals(form.getIsEncrypt())){
//						 String tempfilepath = multiFileToTempFile(attachment);
//						 
//						 IFileEncrypter encry = getEncrypter(form.getEncryptClass());
//			             String encryptpath=encry.encrypt(tempfilepath);	
//			             FileInputStream fin=new FileInputStream(encryptpath);
//			             bt=new byte[fin.available()];
//			             fin.read(bt);
//			             fin.close();
					}else{
						//TODO:按块
						bt=attachment.getBytes();
					}
				} catch (Exception e) {
					e.printStackTrace();
					success = false;
				} 
				form.setAttPath(null);
				success=true;
			}else{
				//将附件上传至服务器磁盘的指定路径下
				success=uploadFile(form.getAttPath(), 
				form.getAttDiskName(), attachment, !"1".equals(form.getIsRelative()),"1".equals(form.getIsEncrypt()),form.getEncryptClass(), true, true);
			}
			//保存成功时，注册附件属性信息
			if(success){
//				//当附件内容被修改时需要更新Form对象的相关属性
//				form.setAttSize(attachment.getSize());
//				updateChangeStatus(form);
//				if(isDb){
//					bscAtService.save(form, "匿名",bt);	
//				}else{
//				    bscAtService.save(form, "匿名");
//				}
				result = true;
			}
		}
		
		return result;
	}
	/**
	 * 将上传的文件临时存在系统的临时文件夹中，可以增加系统上传附件并发访问量
	 * @param attachment
	 * @return
	 * @throws IOException
	 * @throws FileNotFoundException
	 */
	private String multiFileToTempFile(MultipartFile attachment) throws IOException,
			FileNotFoundException {
		String tempfilepath;
		BufferedInputStream in=null;
		BufferedOutputStream out=null;
		File tempFile=null;
		tempFile=File.createTempFile(UUID.randomUUID().toString(),".tmp");
		System.out.println(tempFile.getAbsolutePath());
		int available = 0;
		in = new BufferedInputStream(attachment.getInputStream(), BUFFER_SIZE);
		out = new BufferedOutputStream(new FileOutputStream(tempFile), BUFFER_SIZE);
		byte[] buffer = new byte[BUFFER_SIZE];
		while ((available = in.read(buffer)) > 0) {
				if (available < BUFFER_SIZE) {
					out.write(buffer, 0, available);
				} else {
					out.write(buffer, 0, BUFFER_SIZE);
				}
		}
		in.close();
		out.close();
		tempfilepath=tempFile.getAbsolutePath();
		return tempfilepath;
	}
	/**
	 * 读取附件
	 * @param form 附件模型
	 * @param response 响应
	 * @param request 请求
	 * @return 返回下载结果
	 */
	public boolean downFile(BscAttForm form, HttpServletResponse response,
			HttpServletRequest request)throws Exception{
		InputStream in = null;
    	ServletOutputStream out = null;
    	OutputStream outtemp=null;
    	//获取附件路径和附件名称
    	String atPath = form.getAttPath();
    	String atName = form.getAttName();
    	String atCode = form.getAttCode()+"."+form.getAttFormat();
    	if((atPath == null || atPath.trim().length() == 0
    			|| atName == null || atName.trim().length() == 0)&&!"1".equals(form.getIsDbStore()))
    		return false;
    	
    	//根路径
    	String directoryPath = request.getRealPath("/");
    	//模板文件存放的绝对路径
    	String ntkoTemplateFilePath = directoryPath;// + NtkoUtils.BLANK_DOC_PATH;
    	boolean retState=true;
        	File file = null;
        	byte[] content=null;
        	boolean newFile=false;
//        	如果是新建附件文件，且该附件文件为office类型的文件，则根据文件的扩展名新建一个office类型的模板文件
//            if(AttUtils.isNewFile(atPath, atCode, false, directoryPath, true) && AttUtils.isFileCanBeOpenedByNtko(atCode)){
//            	//获取模板文件
//            	file = AttUtils.newTemplateFile(atCode, ntkoTemplateFilePath);
//            	newFile=true;
//            }else 
            if(!"1".equals(form.getIsDbStore())){
            	file = AttUtils.openFile(atPath, atCode, "0".equals(form.getIsRelative()), directoryPath, true, true);
            }else{
            }
            try {
            
        	//重置response
            response.reset();
            response.setContentType("application/octet-stream");
            
            atName = AttUtils.reEncodeChineseFileName(atName, getRequest());
            response.setHeader("Content-Disposition","attachment; filename=" + atName);
	        if(file!=null){
	        	boolean isEcrypt="1".equals(form.getIsEncrypt());
	        	if(!isEcrypt||newFile){
		            in = new BufferedInputStream(new FileInputStream(file), BUFFER_SIZE);
	            }else{
//	            	IFileEncrypter encrypter=getEncrypter(form.getEncryptClass());	
//	            	String tempfile=encrypter.decrypt(file.getAbsolutePath());
//			        in = new BufferedInputStream(new FileInputStream(tempfile), BUFFER_SIZE);
	            }
	        }else if(content!=null){
	        	in=new ByteArrayInputStream(content);
	        	if("1".equals(form.getIsEncrypt())){
	        		File tempFile=File.createTempFile(UUID.randomUUID().toString(),".tmp");
	        		outtemp=new FileOutputStream(tempFile);
	        		byte[] buffer = new byte[BUFFER_SIZE];
		        	int available= 0;
		        	while ((available= in.read(buffer)) != -1) {
		        		if (available < BUFFER_SIZE) {
		        			outtemp.write(buffer, 0, available);
		        		} else {
		        			outtemp.write(buffer, 0, BUFFER_SIZE);
		        		}
		        	}
//		        	IFileEncrypter encrypter=getEncrypter(form.getEncryptClass());	
//	        		String deCryptFilePath=encrypter.decrypt(tempFile.getAbsolutePath());
//	        		in = new BufferedInputStream(new FileInputStream(deCryptFilePath), BUFFER_SIZE);
	            }
            }
	        out = response.getOutputStream();
	        byte[] buffer = new byte[BUFFER_SIZE];
	        int available= 0;
	        while ((available= in.read(buffer)) != -1) {
	        	if (available < BUFFER_SIZE) {
	        		out.write(buffer, 0, available);
	        	} else {
	        		out.write(buffer, 0, BUFFER_SIZE);
	        	}
	        }
            
        } catch (Exception ex) {
        	retState=false;
        	logger.error("单个附件下载出错,下载附件的form:{}。",JsonUtils.toJson(form));
        	throw ex;
        } finally {
			try {
				if(in != null) {
					in.close();
				}
				if(out != null) {
					out.close();
				}
				if(outtemp != null) {
					outtemp.close();
				}
			} catch (Exception e) {
				logger.error("关闭流出错。",JsonUtils.toJson(form));
			}
		}
        
		return retState;
	}
	/**
	 * 删除服务器磁盘上的物理文件
	 * @param filePath 附件上传的服务器磁盘路径
	 * @param fileName 文件名（含后缀名）
	 * @param isAbsoluteFilePath 是否为绝对路径。true表示为filePath为服务器的绝对路径，false表示为当前应用下的相对路径
	 * @return true表示删除附件成功或附件不存在，false表示删除附件失败
	 */
	@SuppressWarnings("deprecation")
	public boolean deleteFile(String atPath, String atDiskName, boolean isAbsoluteFilePath)throws Exception{
		boolean result = false;
		
		InputStream in = null;
		OutputStream ou = null;
		
		try{
			if(atPath != null && atPath.trim().length() > 0
					&& atDiskName != null && atDiskName.trim().length() > 0){
				
				// 文件路径格式处理
				atPath = atPath.replace('/', '\\');

				// 完整上传路径
				String wholeFilePath = isAbsoluteFilePath ?atPath : this.getRequest().getRealPath("\\") + 
						atPath + "\\" + atDiskName;
				
				File file = new File(wholeFilePath);
				
				//如果文件存在则删除
				if(file.exists())
					result = file.delete();
				else
					result = true;
			}
		}catch(Exception e){
			throw e;
		} finally {
			try {
				if(in != null) {
					in.close();
				}
				if(ou != null) {
					ou.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		return result;
	}
	
	/**
	 * 将附件上传至服务器磁盘的指定路径下
	 * @param filePath 附件上传的服务器磁盘路径
	 * @param fileName 文件名
	 * @param attachment 页面上传的附件对象
	 * @param isAbsoluteFilePath 是否为绝对路径。true表示为filePath为服务器的绝对路径，false表示为当前应用下的相对路径
	 * @param autoBuildDir true表示当文件路径不存在时自动创建
	 * @param canCover true表示当指定文件已经存在时可覆盖原有文件
	 * @return true表示上传成功，false表示上传失败
	 */
	public boolean uploadFile(String atPath, String atDiskName, MultipartFile attachment,
			boolean isAbsoluteFilePath,boolean isEcrypt,String encrypter, boolean autoBuildDir, boolean canCover)throws Exception {
		boolean result = false;
		
		if(attachment == null 
				|| attachment.getSize() == 0 || atPath == null || atPath.trim().length() == 0){
			return result;		
		}

		InputStream in = null;
		OutputStream out = null;

		try {
			// 文件路径格式处理
			atPath = atPath.replace('/', '\\');

			// 上传附件文件夹路径
			String directoryPath = (isAbsoluteFilePath ? "" : this.getRequest().getRealPath("\\")) + atPath;
			
			System.out.println(directoryPath);

			// 完整上传路径
			String wholeFilePath = directoryPath + "\\" + atDiskName;

			// 如果文件目录不存在则创建
			if(autoBuildDir){
				File dir = new File(directoryPath)	;
				if(!dir.exists()){
					dir.mkdirs();
				}
			}

			// 输出附件
			File outputFile = new File(wholeFilePath);
				
			//如果文件不存在则可上传，如果文件存在并且设置为可覆盖则也可上传
			boolean isFileExist = outputFile.exists();
			if(!isFileExist || (isFileExist && canCover)){
					
				boolean canUpload = true; //标识是否能够上传文件
				
				//如果文件存在要先删除，如果文件不存在则标识为可上传
				if(isFileExist){
					canUpload = outputFile.delete();
				}
				if(canUpload){
					// 保存附件至服务器
					if(isEcrypt){
		            }else{
		            	int available = 0;
						in = new BufferedInputStream(attachment.getInputStream(), BUFFER_SIZE);
						out = new BufferedOutputStream(new FileOutputStream(outputFile), BUFFER_SIZE);
						byte[] buffer = new byte[BUFFER_SIZE];
						while ((available = in.read(buffer)) > 0) {
								if (available < BUFFER_SIZE) {
									out.write(buffer, 0, available);
								} else {
									out.write(buffer, 0, BUFFER_SIZE);
								}
						}
		            }
					
					
					result = true;//上传附件成功
				}
			}
		} catch (Exception e) {
			throw e;
		} finally {
			try {
				if(in != null) {
					in.close();
				}
				if(out != null) {
					out.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		return result;
	}
	
	

	/**
	 * 下载文件 
	 * @param response 响应
	 * @param atPath 文件路径 
	 * @param atName 文件名称
	 */
	public boolean downFile(HttpServletResponse response,String atPath, String atName)throws Exception {
		boolean retState=true;
		try {
			String path = atPath + atName;
			File file = new File(path);
			if (file.exists()) {
				InputStream ins = new FileInputStream(path);
				BufferedInputStream bins = new BufferedInputStream(ins);// 放到缓冲流里面
				OutputStream outs = response.getOutputStream();// 获取文件输出IO流
				BufferedOutputStream bouts = new BufferedOutputStream(outs);
				response.setContentType("application/octet-stream");// 设置response内容的类型
				response.setHeader(
						"Content-disposition",
						"attachment;filename="
								+ URLEncoder.encode(atName, "UTF-8"));// 设置头部信息
				int bytesRead = 0;
				final int bufferSize=8192;
				byte[] buffer = new byte[bufferSize];
				// 开始向网络传输文件流
				while ((bytesRead = bins.read(buffer, 0, bufferSize)) != -1) {
					bouts.write(buffer, 0, bytesRead);
				}
				bouts.flush();// 这里一定要调用flush()方法
				ins.close();
				bins.close();
				outs.close();
				bouts.close();
			} else {
				response.sendRedirect("../error.jsp");
				retState=false;
			}
		} catch (IOException e) {
			retState=false;
			throw e;
		}
		return retState;
	}
	


	/**
	 * 当附件内容被修改时需要更新Form对象的相关属性
	 * @param form 附件模型
	 */
	public void updateChangeStatus(BscAttForm form){
		if(form != null && form.getId() != null){
			form.setCreater("匿名");
			form.setCreateTime(new Date());
			form.setAttPath(form.getNewAttPath() != null ? form.getNewAttPath() : form.getAttPath());
			form.setAttName(form.getNewAttName() != null ? form.getNewAttName() : form.getAttName());
		}
	}
	
	public HttpServletRequest getRequest(){
		return ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
	}

}
